home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************
- * Receive.c: File reception routines for xprzmodem.library;
- * Version 2.10, 12 February 1991, by Rick Huebner.
- * Based closely on Chuck Forsberg's rz.c example ZModem code,
- * but too pervasively modified to even think of detailing the changes.
- * Released to the Public Domain; do as you like with this code.
- *
- * Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
- * Version 2.53, 28 June 1993, several additions by Olaf `Olsen' Barthel
- **********************************************************************/
-
- /* Find the end of the string. */
- STATIC STRPTR __regargs find_end(register STRPTR buf)
- {
- while (*buf)
- buf++;
-
- return (buf);
- }
-
- /* Convert octal number to decimal. */
- STATIC ULONG __regargs from_octal(STRPTR buf)
- {
- register ULONG value = 0;
- register UBYTE c;
-
- while (c = *buf)
- {
- if (c == ' ' || c == ',' || c == '\t' || c == '\r' || c == '\n')
- buf++;
- else
- break;
- }
-
- while (c = *buf++)
- {
- if (c >= '0' && c <= '7')
- value = (value << 3) + (c - '0');
- else
- break;
- }
-
- return (value);
- }
-
- /* Skip all blanks. */
- STATIC STRPTR __regargs skip_chars(register STRPTR buf)
- {
- register UBYTE c;
-
- while (c = *buf)
- {
- if (c == ' ' || c == ',' || c == '\t' || c == '\r' || c == '\n')
- {
- buf++;
-
- while (c = *buf)
- {
- if (c == ' ' || c == ',' || c == '\t' || c == '\r' || c == '\n')
- buf++;
- else
- return (buf);
- }
-
- return (buf);
- }
- else
- buf++;
- }
-
- return (buf);
- }
-
- /**********************************************************
- * long XProtocolReceive(struct XPR_IO *xio)
- *
- * Main file reception routine; called by comm program
- **********************************************************/
- long __saveds __asm XProtocolReceive(register __a0 struct XPR_IO *xio)
- {
- struct SetupVars *sv;
- struct Vars *v;
- UBYTE err = FALSE;
-
- /* Perform common setup and initializations */
- if (!(v = setup(xio)))
- return XPRS_FAILURE;
-
- /* Remember data direction for cps rate calculation. */
- v->Receiving = TRUE;
- v->Tryzhdrtype = ZRINIT;
- v->Rxtimeout = 100;
-
- sv = (void *) v->io.xpr_data;
- if (sv->bufpos)
- {
- v->Modemchar = v->Modembuf;
- if (sv->buflen > sizeof(v->Modembuf))
- sv->buflen = sizeof(v->Modembuf);
- memcpy(v->Modembuf, sv->bufpos, sv->buflen);
- v->Modemcount = sv->buflen;
- }
-
- /* Transfer the files */
- if (rcvbatch(v) == ERROR)
- {
- upderr(v, GetLocaleString(MSG_DOWNLOAD_CANCELLED_OR_TIMED_OUT_TXT));
- err = TRUE;
- }
- else
- updmsg(v, GetLocaleString(MSG_DONE_TXT));
-
- /* Clean up and return */
- if (v->io.xpr_setserial && v->Oldstatus != -1)
- (*v->io.xpr_setserial) (v->Oldstatus);
-
- /* If an error occured, it's quite likely that there is
- * garbage in the receive buffer we don't want the user
- * to see.
- */
- if(xio->xpr_sflush)
- (*xio->xpr_sflush)();
-
- FreeMem(v->Filebuf, v->Filebufmax);
- FreeMem(v, (long) sizeof(struct Vars));
-
- return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
- } /* End of long XProtocolReceive() */
-
- /**********************************************************
- * short rcvbatch(struct Vars *v)
- *
- * Start the batch transfer
- **********************************************************/
- short rcvbatch(struct Vars *v)
- {
- switch (tryz(v))
- {
- case ZCOMPL:
- return OK;
- case ZFILE:
- if (rzfiles(v) == OK)
- return OK;
- }
- canit(v);
- return ERROR;
- } /* End of short rcvbatch() */
-
- /**********************************************************
- * short tryz(struct Vars *v)
- *
- * Negotiate with sender to start a file transfer
- **********************************************************/
- short tryz(struct Vars *v)
- {
- short n, errors = 0;
-
- for (n = v->ErrorLimit; --n >= 0;)
- {
- /* Set max frame length and capability flags */
- stohdr(v, (long) v->Tframlen);
- v->Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
-
- if (v->Zctlesc)
- v->Txhdr[ZF0] |= TESCCTL;
-
- zshhdr(v, v->Tryzhdrtype);
- sendbuf(v);
-
- if (v->Tryzhdrtype == ZSKIP) /* Don't skip too far. */
- v->Tryzhdrtype = ZRINIT; /* CAF 8-21-87 */
-
- again:
- /* Check for abort from comm program */
- if (v->io.xpr_chkabort)
- {
- long result = (*v->io.xpr_chkabort) ();
-
- /* Cancel transmission. */
- if (result < 0)
- {
- zshhdr(v, ZABORT);
- sendbuf(v);
- return ERROR;
- }
- else
- {
- /* Skip current file. */
- if (result > 0)
- {
- zshhdr(v, ZSKIP);
- sendbuf(v);
- }
- }
- }
-
- switch (zgethdr(v))
- {
- case ZFILE: /* File name and info packet */
- v->Zconv = v->Rxhdr[ZF0]; /* Suggested txt mode; ZCNL = text, */
- /* ZCBIN = binary, 0 = don't know. */
- v->Zmanag = v->Rxhdr[ZF1]; /* Suggested file management mode. */
- v->Ztrans = v->Rxhdr[ZF2]; /* Suggested file transport mode. */
- v->Tryzhdrtype = ZRINIT;
-
- if (zrdata(v, v->Pktbuf, v->ksize) == GOTCRCW)
- return ZFILE;
- zshhdr(v, ZNAK); /* Packet mangled, ask for retry */
- sendbuf(v);
- goto again;
- case ZSINIT: /* Special attention-grabbing string to use to */
- v->Zctlesc = TESCCTL & v->Rxhdr[ZF0];
- /* interrupt sender */
- if (zrdata(v, v->Attn, ZATTNLEN) == GOTCRCW)
- zshhdr(v, ZACK);
- else
- zshhdr(v, ZNAK);
- sendbuf(v);
- goto again;
- case ZFREECNT: /* Sender wants to know how much room we've got */
- stohdr(v, getfree(v));
- zshhdr(v, ZACK);
- sendbuf(v);
- goto again;
- case ZCOMMAND: /* Sender wants us to do remote commands, */
- /* but we don't do requests. */
-
- if (zrdata(v, v->Pktbuf, v->ksize) == GOTCRCW)
- {
- mysprintf(v->Msgbuf, GetLocaleString(MSG_IGNORING_COMMAND_TXT), v->Pktbuf);
- upderr(v, v->Msgbuf); /* Ignore and report all uploaded commands */
- stohdr(v, 0L); /* whilst telling sender they worked; */
- do
- {
- zshhdr(v, ZCOMPL); /* paranoia can be good for you... */
- sendbuf(v);
- }
- while (++errors < v->ErrorLimit && zgethdr(v) != ZFIN);
- ackbibi(v);
- return ZCOMPL;
- }
- else
- zshhdr(v, ZNAK);
- sendbuf(v);
- goto again;
- case ZCOMPL:
- goto again;
- case ZFIN: /* Sender has ended batch */
- ackbibi(v);
- return ZCOMPL;
- case ZCAN:
- case RCDO:
- upderr(v, v->Msgbuf);
- return ERROR;
- }
- }
- return ERROR;
- } /* End of short tryz() */
-
- /**********************************************************
- * short rzfiles(struct Vars *v)
- *
- * Receive a batch of files
- **********************************************************/
- short rzfiles(struct Vars *v)
- {
- struct SetupVars *sv;
- short c;
-
- /* Keep receiving files until end of batch or error */
- while (TRUE)
- {
- switch (c = rzfile(v))
- {
- case ZEOF:
- case ZSKIP:
- switch (tryz(v))
- {
- case ZCOMPL:
- return OK;
- default:
- return ERROR;
- case ZFILE:
- break;
- }
- break;
- default:
- bfclose(v);
- sv = (void *) v->io.xpr_data;
- if (*sv->option_k == 'N' && v->io.xpr_extension >= 2
- && v->io.xpr_unlink)
- {
- updmsg(v, GetLocaleString(MSG_DELETING_PARTIALLY_RECEIVED_FILE_TXT));
- (*v->io.xpr_unlink) (v->Filename);
- }
- else
- updmsg(v, GetLocaleString(MSG_KEEPING_PARTIALLY_RECEIVED_FILE_TXT));
- return c;
- }
- }
- } /* End of short rzfiles() */
-
- /**********************************************************
- * short rzfile(struct Vars *v)
- *
- * Receive one file; file name packet already read into
- * Pktbuf by tryz()
- **********************************************************/
- short rzfile(struct Vars *v)
- {
- short c, n;
- long result, abort = 0;
-
- /* Process file name packet; either open file and prepare to receive,
- * or tell us to skip this one.
- */
- if (procheader(v) == ERROR)
- return v->Tryzhdrtype = ZSKIP;
-
- n = v->ErrorLimit;
- v->Rxbytes = v->Strtpos;
- v->Eofseen = FALSE;
-
- /* Receive ZDATA frames until finished */
- while (TRUE)
- {
- stohdr(v, v->Rxbytes); /* Tell sender where to start frame */
- zshhdr(v, ZRPOS);
- sendbuf(v);
- nxthdr:
- /* Check for abort from comm program */
- if (v->io.xpr_chkabort)
- {
- if (abort)
- {
- result = abort;
- abort = 0;
- }
- else
- result = (*v->io.xpr_chkabort) ();
-
- /* Cancel transmission. */
- if (result < 0)
- {
- zshhdr(v, ZABORT);
- sendbuf(v);
- }
- else
- {
- /* Skip current file. */
- if (result > 0)
- {
- zshhdr(v, ZSKIP);
- sendbuf(v);
- }
- }
- }
-
- switch (c = zgethdr(v)) /* Wait for frame header */
- {
- default:
- return ERROR;
- case ZFIN:
- zshhdr(v, ZCOMPL);
- sendbuf(v);
- return ZFIN;
- case ZNAK:
- case TIMEOUT:
- if (--n < 0)
- return ERROR;
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
- mysprintf(find_end(v->Msgbuf), GetLocaleString(MSG_AT_X_RETRIES_LEFT_TXT),
- v->Rxbytes, (long) n);
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_timeouts;
- (*v->io.xpr_update) (&v->xpru);
- continue;
- case ZFILE: /* Sender didn't see our ZRPOS yet; try again */
-
- zrdata(v, v->Pktbuf, v->ksize); /* Read and discard redundant */
- continue; /* filename packet */
- case ZEOF: /* End of file data */
- if (v->Rxpos != v->Rxbytes) /* We aren't in sync; go back */
- {
- mysprintf(v->Msgbuf, GetLocaleString(MSG_BAD_EOF_TXT),
- v->Rxbytes, v->Rxpos);
- upderr(v, v->Msgbuf);
- continue;
- }
- bfclose(v); /* All done; close file */
- updmsg(v, GetLocaleString(MSG_EOF_RECEIVED_TXT));
- return c;
- case ERROR: /* Too much garbage while waiting for frame header */
- if (--n < 0)
- return ERROR;
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- mysprintf(find_end(v->Msgbuf), GetLocaleString(MSG_AT_X_RETRIES_LEFT_TXT),
- v->Rxbytes, (long) n);
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_errors;
- (*v->io.xpr_update) (&v->xpru);
- zmputs(v, v->Attn);
- continue;
- case ZSKIP:
- v->Modtime = 1;
- bfclose(v);
- upderr(v, GetLocaleString(MSG_SKIP_COMMAND_RECEIVED_TXT));
- return(c);
- case ZDATA: /* More file data packets forthcoming */
- if (v->Rxpos != v->Rxbytes) /* We aren't in sync; go back */
- {
- if (--n < 0)
- return ERROR;
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- mysprintf(v->Msgbuf, GetLocaleString(MSG_DATA_AT_BAD_POSITION_TXT),
- v->Rxbytes, v->Rxpos);
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_errors;
- (*v->io.xpr_update) (&v->xpru);
- zmputs(v, v->Attn);
- continue;
- }
- /* Receive file data packet(s) */
- moredata:
- /* Give comm program its timeslice if it needs one */
- if (v->io.xpr_chkmisc)
- (*v->io.xpr_chkmisc) ();
- /* Check for abort from comm program */
- if (v->io.xpr_chkabort)
- {
- result = (*v->io.xpr_chkabort)();
-
- if (result < 0)
- {
- zshhdr(v, ZABORT);
- sendbuf(v);
- goto aborted;
- }
- else
- {
- if (result > 0)
- {
- abort = result;
- zshhdr(v, ZSKIP);
- sendbuf(v);
- }
- }
- }
- switch (c = zrdata(v, v->Pktbuf, v->ksize))
- {
- case ZCAN:
- case RCDO:
- aborted:
- upderr(v, GetLocaleString(MSG_TRANSFER_CANCELED_TXT));
- return ERROR;
- case ERROR: /* CRC error or packet too long */
- if (--n < 0)
- return ERROR;
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- mysprintf(find_end(v->Msgbuf), GetLocaleString(MSG_AT_X_RETRIES_LEFT_TXT),
- v->Rxbytes, (long) n);
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_errors;
- (*v->io.xpr_update) (&v->xpru);
- zmputs(v, v->Attn);
- continue;
- case TIMEOUT:
- if (--n < 0)
- return ERROR;
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
- mysprintf(find_end(v->Msgbuf), GetLocaleString(MSG_AT_X_RETRIES_LEFT_TXT),
- v->Rxbytes, (long) n);
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_timeouts;
- (*v->io.xpr_update) (&v->xpru);
- continue;
- case GOTCRCW: /* Sender says it's waiting for an ACK */
- n = v->ErrorLimit;
- if (putsec(v) == ERROR)
- return ERROR;
- stohdr(v, v->Rxbytes);
- zshhdr(v, ZACK);
- sendbuf(v);
- goto nxthdr;
- case GOTCRCQ: /* Sender says it's not waiting, */
- /* but ACK anyway (rarely used) */
- n = v->ErrorLimit;
- if (putsec(v) == ERROR)
- return ERROR;
- stohdr(v, v->Rxbytes);
- zshhdr(v, ZACK);
- sendbuf(v);
- goto moredata;
- case GOTCRCG: /* Sender says keep receiving, there's more coming */
- n = v->ErrorLimit;
- if (putsec(v) == ERROR)
- return ERROR;
- goto moredata;
- case GOTCRCE: /* Sender says this is the last packet */
- n = v->ErrorLimit;
- if (putsec(v) == ERROR)
- return ERROR;
- goto nxthdr;
- }
- }
- }
- } /* End of short rzfile() */
-
- /**********************************************************
- * short procheader(struct Vars *v)
- *
- * Process file name & info packet; either open file and
- * prepare to receive, or return ERROR if we should skip
- * this one for some reason
- **********************************************************/
- short procheader(struct Vars *v)
- {
- struct SetupVars *sv;
- UBYTE *p, *openmode, buff[PATHLEN];
- long n;
-
- openmode = "w";
- v->Strtpos = 0;
-
- /* Extract expected filesize from file info packet, if given */
- v->Fsize = -1;
- p = find_end(v->Pktbuf) + 1;
-
- if (*p)
- {
- STRPTR index, ptr;
-
- index = skip_chars(ptr = p);
-
- v->Fsize = Atol(ptr);
-
- index = skip_chars(ptr = index);
-
- v->Modtime = from_octal(ptr);
-
- skip_chars(ptr = index);
-
- v->Bits = from_octal(ptr);
-
- v->SetAttributes = TRUE;
- }
- else
- {
- v->Modtime = 0;
- v->Bits = 0;
- v->SetAttributes = FALSE;
- }
-
- /* If option RY set, use full received file path */
- sv = (void *) v->io.xpr_data;
- if (*sv->option_r == 'Y')
- strcpy(v->Filename, v->Pktbuf);
- else
- {
- /* else use the default directory path specified in the setup options */
- strcpy(v->Filename, sv->option_p);
- p = v->Filename + strlen(v->Filename) - 1;
- if (p >= v->Filename && *p != '/' && *p != ':')
- *++p = '/';
- *++p = '\0';
- /* Append the filename from the file info packet; ignore anything before
- * last /, \, or : in filename (received directory path is ignored)
- */
- p = find_end(v->Pktbuf); /* start at end and scan back */
- /* to start of name */
- while (p >= v->Pktbuf && *p != '/' && *p != '\\' && *p != ':')
- --p;
- strcat(v->Filename, ++p);
- }
-
- /* Make sure we have room for file; skip it if not. */
-
- if (v->Fsize > getfree(v))
- {
- mysprintf(v->Msgbuf, GetLocaleString(MSG_INSUFFICIENT_DISK_SPACE_TXT),
- v->Fsize, getfree(v));
- upderr(v, v->Msgbuf);
- v->Noroom = TRUE;
- return ERROR;
- }
-
- /* Display name of file being received for user */
- v->xpru.xpru_updatemask = XPRU_FILENAME;
- v->xpru.xpru_filename = (char *) v->Filename;
- (*v->io.xpr_update) (&v->xpru);
-
- /* If a file with this name already exists, handle in
- * accordance with O option
- */
- if (exist(v))
- {
- switch (*sv->option_o)
- {
- case 'N': /* Don't overwrite; change name to prevent collision */
- strcpy(buff, v->Filename);
- strcat(v->Filename, ".dup");
- n = 2;
- while (exist(v))
- {
- mysprintf(v->Filename, "%s.dup%ld", buff, n);
- ++n;
- }
- /* Update filename display to show new name */
- (*v->io.xpr_update) (&v->xpru);
- break;
- case 'R': /* Resume transfer from current end of file */
- openmode = "a";
- v->Strtpos = (*v->io.xpr_finfo) (v->Filename, 1L);
- break;
- case 'S': /* Skip it */
- upderr(v, GetLocaleString(MSG_FILE_ALREADY_EXISTS_TXT));
- return ERROR;
- /* Else 'Y', go ahead and overwrite it (openmode = w) */
- }
- }
-
- /* Set text/binary mode according to options before opening file */
- set_textmode(v);
-
- /* Figure out file translation mode to use; either binary (verbatim
- * transfer) or ASCII (perform end-of-line conversions). If user has
- * specified a mode (TY or TN), that's what we use. If user says use
- * sender's suggestion (T?), set mode according to Zconv flag. If neither
- * side specifies, default to binary mode.
- */
- v->Thisbinary = v->Rxbinary || !v->Rxascii;
- if (!v->Rxbinary && v->Zconv == ZCNL)
- v->Thisbinary = FALSE;
- if (!v->Rxascii && v->Zconv == ZCBIN)
- v->Thisbinary = TRUE;
-
- /* Open the file (finally) */
- if (!(v->File = bfopen(v, openmode)))
- {
- ++v->Errcnt;
- upderr(v, GetLocaleString(MSG_CANT_OPEN_FILE_TXT));
- return ERROR;
- }
- GetSysTime(&v->Starttime);
- v->BytesReceived = 0;
-
- /* Initialize comm program transfer status display */
- v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
- | XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
- | XPRU_BYTES | XPRU_ELAPSEDTIME;
- v->xpru.xpru_protocol = "ZModem";
- v->xpru.xpru_filesize = v->Fsize;
- v->xpru.xpru_msg = (v->Thisbinary) ? GetLocaleString(MSG_RECEIVING_BINARY_FILE_TXT)
- : GetLocaleString(MSG_RECEIVING_TEXT_FILE_TXT);
- v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
- v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
- v->xpru.xpru_bytes = v->Strtpos;
- v->xpru.xpru_blocksize = v->ksize;
- update_rate(v);
- (*v->io.xpr_update) (&v->xpru);
-
- return OK;
- } /* End of short procheader() */
-
- /**********************************************************
- * short putsec(struct Vars *v)
- *
- * Writes the received file data to the output file.
- * If in ASCII mode, stops writing at first ^Z, and converts all
- * \r\n pairs or solo \r's to \n's.
- **********************************************************/
- short putsec(struct Vars *v)
- {
- static char nl = '\n';
- UBYTE *p;
- short n;
-
- /* If in binary mode, write it out verbatim */
- if (v->Thisbinary)
- {
- if (bfwrite(v, v->Pktbuf, (long) v->Rxcount) != v->Rxcount)
- goto diskfull;
- /* If in text mode, perform end-of-line cleanup */
- }
- else
- {
- if (v->Eofseen)
- return OK;
- for (p = v->Pktbuf, n = v->Rxcount; --n >= 0; ++p)
- {
- if (*p == CPMEOF)
- {
- v->Eofseen = TRUE;
- return OK;
- }
- else if (*p != '\n' && v->Lastsent == '\r')
- {
- if (bfwrite(v, &nl, 1L) != 1)
- goto diskfull;
- }
- if (*p != '\r' && bfwrite(v, p, 1L) != 1)
- goto diskfull;
- v->Lastsent = *p;
- }
- }
-
- /* Update comm program status display */
- v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
- | XPRU_ELAPSEDTIME | XPRU_BLOCKCHECK;
- ++v->xpru.xpru_blocks;
- v->xpru.xpru_blocksize = v->Rxcount;
- v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
- v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
- update_rate(v);
- (*v->io.xpr_update) (&v->xpru);
-
- return OK;
-
- diskfull:
- upderr(v, GetLocaleString(MSG_ERROR_WRITING_FILE_TXT));
- v->Noroom = TRUE;
- return ERROR;
- } /* End of short putsec() */
-
- /**********************************************************
- * void ackbibi(struct Vars *v)
- *
- * End of batch transmission; disengage cleanly from sender
- **********************************************************/
- void ackbibi(struct Vars *v)
- {
- short n;
-
- stohdr(v, 0L);
- for (n = 4; --n;)
- {
- zshhdr(v, ZFIN);
- sendbuf(v);
- switch (readock(v, 100))
- {
- case 'O':
- readock(v, 1); /* Discard 2nd 'O' */
- case TIMEOUT:
- case RCDO:
- return;
- }
- }
- } /* End of void ackbibi() */
- /* End of Receive.c source */
-